﻿using Newtonsoft.Json;
using P2P.Main.Model;
using P2P.Main.Util;
using P2P.Network;
using P2P.Network.ExtensionMethods;
using P2P.Network.SocketServer;
using P2P.Util;
using System.Net;
using System.Net.Sockets;
using System.Text;
using static P2P.Network.SocketServer.ServerConnection;

namespace P2P.Main
{
    public sealed class AppBuilder
    {
        static SocketArgsPool socketArgsPool = new SocketArgsPool(500000);
        static BufferPool bufferManager = new BufferPool(500000 * 4096, 4096);

        private AppBuilder() { }

        private void UdpReceivedCallback(Socket sender, DataEventArgs e) 
        {
            try
            {
                byte[] buffer = e.Data;

                if (e.IsEnableAES) 
                {
                    buffer = AESUtil.Decrypt(buffer, SharedModel.Instance.AESKey);
                }
                
                string bufStr = Encoding.UTF8.GetString(buffer);

                Logger.WriteInfo("Received data: " + bufStr);

                object result = ApiHandler.Process(sender, e.RemoteEndPoint, bufStr);

                if (result != null)
                {
                    string msg = JsonConvert.SerializeObject(result, Formatting.None);

                    sender.SendTo(msg, e.RemoteEndPoint);
                }
            }
            catch (Exception ex)
            {
                Logger.WriteError(ex.Message);
            }
        }

        private void TcpSocketConnected(object sender, SocketEventArgs e) 
        {
            SocketAsyncEventArgs args = socketArgsPool.CheckOut();
            bufferManager.CheckOut(args);
            ServerConnection connection = new ServerConnection(e.Socket, args,
                    new DataReceivedCallback(TcpDataReceivedCallback),
                    new DisconnectedCallback(TcpDisconnected));
        }

        private void TcpDataReceivedCallback(ServerConnection sender, DataEventArgs e)
        {
            try
            {
                byte[] buffer = e.Data;
                string bufStr = Encoding.UTF8.GetString(buffer);

                object result = ApiHandler.Process((sender.EventArgs.UserToken as State).socket, e.RemoteEndPoint, bufStr);

                if (result != null)
                {
                    string msg = JsonConvert.SerializeObject(result, Formatting.None);

                    sender.SendData(msg);
                }
            }
            catch (Exception ex)
            {
                Logger.WriteError(ex.Message);
            }
           
        }

        private void TcpDisconnected(ServerConnection sender, SocketAsyncEventArgs e)
        {
            bufferManager.CheckIn(e);
            socketArgsPool.CheckIn(e);
        }


        public void Start()
        {
            UDPSocketListener UdpSocketListener = new UDPSocketListener(SharedModel.Instance.Hosts.UDPMainServerFirstInternalIP,
                SharedModel.Instance.Hosts.UDPMainServerFirstInternalPort.Value, UdpReceivedCallback);
            UdpSocketListener.Start();

            TcpSocketListener TcpSocketListener = new TcpSocketListener(SharedModel.Instance.Hosts.TCPMainServerInternalIP, 
                SharedModel.Instance.Hosts.TCPMainServerInternalPort.Value, 10);
            TcpSocketListener.SocketConnected += TcpSocketConnected;
            TcpSocketListener.Start();

            Console.WriteLine("请输入字符串，输入 q 退出：");

            while (true)
            {
                string input = Console.ReadLine();

                if (input == "q")
                {
                    UdpSocketListener.Stop();
                    TcpSocketListener.Stop();
                    break;
                }
            }
        }

        public static AppBuilder Configure(Hosts hosts,long workerId, long datacenterId,int kbucketCount,int kbucketCapacity,string aesKey) 
        {
            SharedModel.Instance.Hosts = hosts;
            SharedModel.Instance.KBucketCount = kbucketCount;
            SharedModel.Instance.KBucketCapacity = kbucketCapacity;
            SharedModel.Instance.AESKey = aesKey;
            SnowflakeIdGenerator.Instance.Init(workerId, datacenterId);
            return new AppBuilder();
        }
    }
}
